//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package org.geolatte.geom.json;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.type.WritableTypeId;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import java.io.IOException;
import org.geolatte.geom.AbstractGeometryCollection;
import org.geolatte.geom.Complex;
import org.geolatte.geom.Geometry;
import org.geolatte.geom.GeometryType;
import org.geolatte.geom.MultiPolygon;
import org.geolatte.geom.Polygon;
import org.geolatte.geom.Position;
import org.geolatte.geom.crs.CoordinateReferenceSystem;
import org.springframework.boot.jackson.JsonComponent;

/**
 * 从org.geolatte.geom.json.GeometrySerializer 拷贝过来
 * @param <P>
 */
@JsonComponent
public class GeometrySerializer<P extends Position> extends JsonSerializer<Geometry<P>> {
    private final Settings settings;

    public GeometrySerializer(){
        this.settings = new Settings();
        this.settings.override(Setting.SERIALIZE_FEATURE_BBOX, true);
        this.settings.override(Setting.SERIALIZE_FEATURE_COLLECTION_BBOX, true);
        this.settings.override(Setting.SERIALIZE_CRS_AS_URN, true);
        this.settings.override(Setting.SUPPRESS_CRS_SERIALIZATION, false);
        this.settings.override(Setting.IGNORE_CRS, false);
        this.settings.override(Setting.FORCE_DEFAULT_CRS_DIMENSION, false);
    }

    public GeometrySerializer(Settings settings) {
        this.settings = settings;
    }

    public void serialize(Geometry<P> geometry, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        this.writeGeometry(gen, geometry, !this.settings.isSet(Setting.SUPPRESS_CRS_SERIALIZATION));
    }

    public void serializeWithType(Geometry<P> value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
        WritableTypeId typeIdDef = typeSer.writeTypePrefix(gen, typeSer.typeId(value, value.getClass(), JsonToken.VALUE_STRING));
        this.serialize(value, gen, serializers);
        typeSer.writeTypeSuffix(gen, typeIdDef);
    }

    private void writeGeometry(JsonGenerator gen, Geometry<P> geometry, boolean includeCrs) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("type", geometry.getGeometryType().getCamelCased());
        if (includeCrs) {
            this.writeCrs(gen, geometry.getCoordinateReferenceSystem());
        }

        if (geometry.getGeometryType() != GeometryType.GEOMETRYCOLLECTION) {
            this.writeCoords(gen, geometry.getGeometryType(), geometry);
        } else {
            AbstractGeometryCollection<P, Geometry<P>> gc = (AbstractGeometryCollection)geometry;
            this.writeGeometries(gen, gc.components());
        }

        gen.writeEndObject();
    }

    private void writeGeometries(JsonGenerator gen, Geometry<P>[] geometries) throws IOException {
        gen.writeFieldName("geometries");
        gen.writeStartArray();

        for(Geometry<P> g : geometries) {
            this.writeGeometry(gen, g, false);
        }

        gen.writeEndArray();
    }

    private void writeCoords(JsonGenerator gen, GeometryType type, Geometry<P> geom) throws IOException {
        gen.writeFieldName("coordinates");
        double[] buf = new double[geom.getCoordinateDimension()];
        if (geom.isEmpty()) {
            gen.writeStartArray();
            gen.writeEndArray();
        } else {
            if (type == GeometryType.POINT) {
                this.writePosition(gen, geom.getPositionN(0), buf);
            }

            if (type == GeometryType.LINESTRING || type == GeometryType.MULTIPOINT) {
                this.writeLinear(gen, geom, buf);
            }

            if (type == GeometryType.POLYGON || type == GeometryType.MULTILINESTRING) {
                this.writeListOfLinear(gen, (Complex)geom, buf);
            }

            if (type == GeometryType.MULTIPOLYGON) {
                this.writeListOfPolygon(gen, (MultiPolygon)geom, buf);
            }

        }
    }

    private void writeListOfPolygon(JsonGenerator gen, MultiPolygon<P> geom, double[] buf) throws IOException {
        gen.writeStartArray();

        for(Polygon<P> c : (Polygon[])geom.components()) {
            this.writeListOfLinear(gen, c, buf);
        }

        gen.writeEndArray();
    }

    private void writeListOfLinear(JsonGenerator gen, Complex geom, double[] buf) throws IOException {
        gen.writeStartArray();

        for(Geometry<P> c : geom.components()) {
            this.writeLinear(gen, c, buf);
        }

        gen.writeEndArray();
    }

    private void writeLinear(JsonGenerator gen, Geometry<P> geom, double[] buf) throws IOException {
        gen.writeStartArray();

        for(P pos : geom.getPositions()) {
            this.writePosition(gen, pos, buf);
        }

        gen.writeEndArray();
    }

    private void writePosition(JsonGenerator gen, P position, double[] buf) throws IOException {
        gen.writeArray(position.toArray(buf), 0, buf.length);
    }

    private void writeCrs(JsonGenerator gen, CoordinateReferenceSystem<P> crs) throws IOException {
        gen.writeFieldName("crs");
        this.writeNamedCrs(gen, crs);
    }

    private void writeNamedCrs(JsonGenerator gen, CoordinateReferenceSystem<P> crs) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("type", "name");
        gen.writeFieldName("properties");
        if (this.settings.isSet(Setting.SERIALIZE_CRS_AS_URN)) {
            this.writeCrsName(gen, crs.getCrsId().toUrn());
        } else {
            this.writeCrsName(gen, crs.getCrsId().toString());
        }

        gen.writeEndObject();
    }

    private void writeCrsName(JsonGenerator gen, String epsgString) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("name", epsgString);
        gen.writeEndObject();
    }

    @SuppressWarnings("unchecked")
    @Override
    public Class<Geometry<P>> handledType() {
        return (Class<Geometry<P>>) (Class<?>) Geometry.class;
    }
}
